package com.foundationmade.newbiz.core.security.service;

import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.foundationmade.newbiz.core.security.domain.SecurityIdentity;

import devcken.hibernate.query.OrderBy;
import devcken.utility.Pair;

/**
 * 
 * @author Devcken
 */
@Service("securityService")
@Transactional
public class SecurityService
{
	/**
	 * NewBiz {@link org.hibernate.SessionFactory}를 주입받는다.
	 */
	@Resource(name="newbizSecuritySessionFactory")
	private SessionFactory _sessionFactory;
	
	/**
	 * 주어진 검색 조건으로 보안 계정 목록을 조회한다.
	 * @param searchCondition 보안 계정에 대한 검색 조건을 설정한다.
	 * @param page 표시할 페이지를 설정한다.
	 * @param pageSize 한 페이지 당 표시할 레코드 수를 설정한다.
	 * @param sortConditions 정렬 조건을 설정한다.
	 * @param identities 조회된 보안 계정 목록을 참조한다.
	 * @return 검색 조건으로 조회된 전체 보안 계정의 개수가 반환된다.
	 */
	@SuppressWarnings("unchecked")
	public Integer listSecurityIdentities(SecurityIdentity searchCondition,
			int page,
			int pageSize, 
			List<Pair<String, OrderBy>> sortConditions,
			List<SecurityIdentity> identities)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			Criteria criteria = session.createCriteria(SecurityIdentity.class);
			
			if (StringUtils.isNotEmpty(searchCondition.getSecurityId()))
			{
				criteria = criteria.add(Restrictions.like("securityId", searchCondition.getSecurityId()));
			}
			
			if (StringUtils.isNotEmpty(searchCondition.getName()))
			{
				criteria = criteria.add(Restrictions.like("name", searchCondition.getName()));
			}
			
			if (StringUtils.isNotEmpty(searchCondition.getPhoneNumber()))
			{
				criteria = criteria.add(Restrictions.like("phoneNumber", searchCondition.getPhoneNumber()));
			}
			
			if (StringUtils.isNotEmpty(searchCondition.getCellNumber()))
			{
				criteria = criteria.add(Restrictions.like("cellNumber", searchCondition.getCellNumber()));
			}
			
			if (sortConditions.size() == 0)
			{
				criteria.addOrder(Order.desc("generatedTimestamp"));
			}
			
			for (int index = 0; index < sortConditions.size(); index++)
			{
				Pair<String, OrderBy> sortCondition = sortConditions.get(index);
				
				switch (sortCondition.getRight())
				{
					case Ascending:
						
						criteria.addOrder(Order.asc(sortCondition.getLeft()));
						
						break;
						
					case Descending:
						
						criteria.addOrder(Order.desc(sortCondition.getLeft()));
						
						break;
				}
			}
			
			Integer filteredCount = ((Long)criteria.setProjection(Projections.rowCount()).uniqueResult()).intValue();
			
			criteria.setProjection(null);
			criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
			
			criteria.setFirstResult(pageSize * (page - 1)).setMaxResults(pageSize);
			
			identities.addAll(criteria.list());
			
			return filteredCount;
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 보안 계정 전체 목록을 조회한다.
	 * @return 보안 계정 전체 목록이 반환된다.
	 */
	@SuppressWarnings("unchecked")
	public List<SecurityIdentity> listSecurityIdentities()
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			Criteria criteria = session.createCriteria(SecurityIdentity.class)
					.add(Restrictions.eq("enabled", true))
					.add(Restrictions.eq("locked", false));
			
			criteria.addOrder(Order.asc("name"));
			
			List<SecurityIdentity> identities = criteria.list();
			
			return identities;
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 보안 계정의 개수를 가져온다.
	 * @return 보안 계정의 개수가 반환된다.
	 */
	public Long countSecurityIdentities()
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			return (Long)session.createCriteria(SecurityIdentity.class).setProjection(Projections.rowCount()).uniqueResult();
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 주어진 보안 아이디에 대한 보안 계정을 찾는다.
	 * @param securityId 보안 아이디를 설정한다.
	 * @return 주어진 보안 아이디에 대한 보안 계정이 존재하면 true, 그렇지 않으면 false가 반환된다. 
	 */
	public boolean findSecurityIdentityBy(String securityId)
	{
		return this.getSecurityIdentity(securityId) != null;
	}
	
	/**
	 * 주어진 보안 아이디에 대한 보안 계정 정보를 열람한다.
	 * @param securityId 열람하려는 보안 계정 정보의 보안 아이디를 설정한다.
	 * @return 주어진 보안 아이디에 대한 보안 계정 정보가 반환된다. 존재하지 않을 경우 null 반환된다.
	 */
	public SecurityIdentity getSecurityIdentity(String securityId)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			Criteria criteria = session.createCriteria(SecurityIdentity.class)
					.add(Restrictions.eq("securityId", securityId));
			
			SecurityIdentity identity = (SecurityIdentity)criteria.uniqueResult();
			
			return identity;
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 주어진 보안 계정 아이디에 대한 보안 계정 정보를 열람한다.
	 * @param identityId 열람하려는 보안 계정 정보의 보안 계정 아이디를 설정한다.
	 * @return 주어진 보안 계정 아이디에 대한 보안 계정 정보가 반환된다. 존재하지 않을 경우 null이 반환된다.
	 */
	public SecurityIdentity getSecurityIdentity(Long identityId)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			Criteria criteria = session.createCriteria(SecurityIdentity.class)
					.add(Restrictions.eq("identityId", identityId));
			
			SecurityIdentity identity = (SecurityIdentity)criteria.uniqueResult();
			
			// 보안 상 보안 비밀번호를 제거한다.
			identity.setSecurityPassword(null);
			
			return identity;
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 보안 계정 정보를 저장한다.
	 * @param identity 저장하려는 보안 계정 정보를 설정한다.
	 * @return 보안 계정 정보가 저장되면 true, 그렇지 않으면 false가 반환된다.
	 */
	public boolean saveSecurityIdentity(SecurityIdentity identity)
	{
		Session session = this._sessionFactory.openSession();
		
		Transaction transaction = session.beginTransaction();
		
		try
		{
			session.saveOrUpdate(identity);
			
			transaction.commit();
			
			return true;
		}
		catch (Exception error)
		{
			transaction.rollback();
			
			throw error;
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
}